﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework;
namespace BloodBot
{
    public class Cell : Microsoft.Xna.Framework.GameComponent
    {
        public int row, col;
        public string type, status;
        public float size;
        public Model activeModel;
        public Model debugSphere;
        public BoundingBox BoundBox;
        public Vector3 pos;
        public Matrix cellTranslation;
        public Matrix cellModelTranslation;
        public Matrix cellScale;


        private int cost;
        private int heuristicCost;
        private Cell parentCell;
        private bool isPath;

        public Cell(Game game, int row, int col, float x, float y, float z, string type, string status, float size) : base(game)
        {
            this.row = row;
            this.col = col;
            this.size = size;
            this.type = type;
            this.status = status;
            isPath = false;
            setIsPath(false);
            setPosition(x, y, z);
            setType(type);
            
        }

        public string getStatus()
        {
            return status;
        }

        public void setType(string t)
        {
            if (t == "Wall")
            {
                this.type = t;
                activeModel = Game.Content.Load<Model>("Models\\Wall");
                BoundBox = new BoundingBox(
                    new Vector3(pos.X - 0.5f, pos.Y, pos.Z - 0.5f),
                    new Vector3(pos.X + 0.5f, pos.Y + 1.0f, pos.Z + +0.5f));
                cellScale += Matrix.CreateScale(size * 1.0f);
            }
            if (t == "Ground")
            {
                this.type = t;
                activeModel = null;
                BoundBox = new BoundingBox();
                //the ground is already visible in the BloodCell.cs so no model needed

                //Following is incase you need to see the whole level grid, not just non-ground types
                //this.BoundBox = new BoundingBox(
                //new Vector3(pos.X - 0.5f, pos.Y, pos.Z - 0.5f),
                //new Vector3(pos.X + 0.5f, pos.Y + 1.0f, pos.Z + +0.5f));
            }
            if (t == "Enemy")
            {
                //Enemy Spawn code goes here
            }
            if (t == "FuzzBall")
            {
                this.type = t;
                activeModel = Game.Content.Load<Model>("Models\\FuzzBall");
                this.BoundBox = new BoundingBox(
                     new Vector3(pos.X - 0.5f, pos.Y, pos.Z - 0.5f),
                     new Vector3(pos.X + 0.5f, pos.Y + 1.0f, pos.Z + +0.5f));
                cellScale += Matrix.CreateScale(size * 0.09f);
                cellModelTranslation = Matrix.CreateTranslation(0, 0.5f, 0);
                cellTranslation *= cellModelTranslation;
            }
                
            //more to go here
        }

        public void setIsPath(bool s)
        {
            if (s)
            {
                isPath = true;
                debugSphere = Game.Content.Load<Model>("Models\\DebugSphere");
            }
            else if (!s)
                isPath = false;
        }

        public string getType()
        {
            return type;
        }

        public BoundingBox getBoundingBox()
        {
            return BoundBox;
        }

        public string customToString()
        {
            return "Cell " + pos.ToString() + " : " + row + ", " + col + " : " + type + " " + status;
        }

        public void setPosition(float x, float y, float z)
        {
            this.pos = new Vector3(x, y, z);
            cellTranslation = Matrix.CreateTranslation(pos);
            
        }

        public void Draw(Camera camera, bool debug)
        {
            if (activeModel != null)
            {
                // Copy any parent transforms.
                Matrix[] transforms = new Matrix[activeModel.Bones.Count];
                activeModel.CopyAbsoluteBoneTransformsTo(transforms);

                // Draw the model. A model can have multiple meshes, so loop.
                foreach (ModelMesh mesh in activeModel.Meshes)
                {
                    // This is where the mesh orientation is set, as well 
                    // as our camera and projection.
                    foreach (BasicEffect effect in mesh.Effects)
                    {
                        effect.EnableDefaultLighting();
                        effect.World = transforms[mesh.ParentBone.Index]
                           * cellScale
                           * cellTranslation;
                        effect.View = camera.view;
                        effect.Projection = camera.projection;
                    }
                    mesh.Draw();
                }
            }

            if (isPath && debug)
            {
                // Copy any parent transforms.
                Matrix[] transforms = new Matrix[debugSphere.Bones.Count];
                debugSphere.CopyAbsoluteBoneTransformsTo(transforms);

                // Draw the model. A model can have multiple meshes, so loop.
                foreach (ModelMesh mesh in debugSphere.Meshes)
                {
                    // This is where the mesh orientation is set, as well 
                    // as our camera and projection.
                    foreach (BasicEffect effect in mesh.Effects)
                    {
                        effect.EnableDefaultLighting();
                        effect.World = transforms[mesh.ParentBone.Index] * Matrix.CreateScale(0.5f)
                           * cellTranslation * Matrix.CreateTranslation(new Vector3(0, 1.3f, 0));
                        effect.View = camera.view;
                        effect.Projection = camera.projection;
                    }
                    mesh.Draw();
                }
            }
        }


        public void Disable()
        {
            //Changes the state of the cell to disabled, in case the cell is out of bounds or other reasons
            status = "Disabled";
            type = "Disabled";
            activeModel = null;
            BoundBox = new BoundingBox(Vector3.Zero, Vector3.Zero);

            //more to go here
        }

        public bool IsAdjacentToCell(Cell otherCell)
        {
            if (this.row == otherCell.row && this.col == otherCell.col)
            {
                return false;
            }

            if ((otherCell.row == this.row || otherCell.row == this.row - 1 || otherCell.row == this.row + 1) &&
                (otherCell.col == this.col || otherCell.col == this.col - 1 || otherCell.col == this.col + 1))
            {
                return true;
            }
            return false;
        }

        public bool IsDiagonalToCell(Cell otherCell)
        {
            if (this.row == otherCell.row + 1 && this.col == otherCell.col + 1)
                return true;
            if (this.row == otherCell.row - 1 && this.col == otherCell.col - 1)
                return true;
            if (this.row == otherCell.row + 1 && this.col == otherCell.col - 1)
                return true;
            if (this.row == otherCell.row - 1 && this.col == otherCell.col + 1)
                return true;
            return false;


        }

        public int GetCost()
        {
            return cost;
        }

        public void SetCost(int newCost)
        {
            this.cost = newCost;
        }

        public void AddToCost(int addition)
        {
            this.cost += addition;
        }

        public void SetParentCell(Cell cell)
        {
            this.parentCell = cell;
        }

        public Cell GetParentCell()
        {
            return parentCell;
        }

        public int GetHeuristicCost()
        {
            return heuristicCost;
        }

        public void SetHeuristicCost(int newCost)
        {
            this.heuristicCost = newCost;
        }

        public bool ContainsVector(Vector3 location)
        {
            if ( (location.X > pos.X - 0.5f) && (location.X <= pos.X + 0.5f) &&
                //(location.Y > pos.Y) && (location.Y <= pos.Y + 1.0f) &&
                (location.Z > pos.Z - 0.5f) && (location.Z <= pos.Z + 0.5f))
            {
                return true;
            }
            return false;
        }
    }
}
